package com.jin.jpa.wrapper;

import com.jin.jpa.wrapper.expression.GroupByExpression;
import com.jin.jpa.wrapper.expression.OrderByExpression;
import com.jin.jpa.wrapper.support.FieldFunction;
import com.jin.jpa.wrapper.util.EntityManagerUtils;
import com.jin.jpa.wrapper.util.LambdaUtils;

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import java.beans.PropertyDescriptor;
import java.util.List;

/**
 * @Author: yuanxiuyao
 * @Date: 2024/1/4 15:13
 * @PackageName: org.example.jpa.wrapper.expression
 * @Version 1.0
 */
public class LambdaWrapper<T> extends AbstractWrapper<T> {

    private final EntityManager entityManager = EntityManagerUtils.entityManager;

    public LambdaWrapper(Class<T> instanceClass){
        super(instanceClass);
    }

    public static <T> LambdaWrapper<T> of(Class<T> instanceClass){
        return new LambdaWrapper<T>(instanceClass);
    }

    @Override
    public LambdaWrapper<T> orderByAsc(FieldFunction<T, ?> expression){
        PropertyDescriptor propertyDescriptor = LambdaUtils.extract(expression, instanceClass);
        expressionList.add(OrderByExpression.asc(propertyDescriptor.getName()));
        return this;
    }

    @Override
    public LambdaWrapper<T> orderByDesc(FieldFunction<T, ?> expression){
        PropertyDescriptor propertyDescriptor = LambdaUtils.extract(expression, instanceClass);
        expressionList.add(OrderByExpression.desc(propertyDescriptor.getName()));
        return this;
    }

    @Override
    public LambdaWrapper<T> groupBy(FieldFunction<T, ?> expression){
        PropertyDescriptor propertyDescriptor = LambdaUtils.extract(expression, instanceClass);
        expressionList.add(GroupByExpression.of(propertyDescriptor.getName()));
        return this;
    }

    @Override
    public <R> R fetchOne(){
        List<R> resultList = fetchList();
        return resultList == null && resultList.isEmpty() ? null : resultList.get(0);
    }

    @Override
    public <R> List<R> fetchList(){
        TypedQuery<R> typedQuery = getObjectTypedQuery();
        return typedQuery.getResultList();
    }

    @Override
    public T one() {
        List<T> list = list();
        return list == null && list.isEmpty() ? null : list.get(0);
    }

    @Override
    public List<T> list() {
        TypedQuery<T> typedQuery = getInstanceTypedQuery();
        return typedQuery.getResultList();
    }

    public <R> TypedQuery<R> getObjectTypedQuery(){
        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
        CriteriaQuery<Object[]> query = criteriaBuilder.createQuery(Object[].class);
        Root<T> root = query.from(instanceClass);
        LambdaWrapperContext<T> lambdaWrapperContext = LambdaWrapperContext.of(criteriaBuilder, query, root);
        multiselect(lambdaWrapperContext);
        where(lambdaWrapperContext);
        groupBy(lambdaWrapperContext);
        orderBy(lambdaWrapperContext);
        return (TypedQuery<R>) entityManager.createQuery(query);
    }

    public TypedQuery<T> getInstanceTypedQuery(){
        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
        CriteriaQuery<T> query = criteriaBuilder.createQuery(getInstanceClass());
        Root<T> root = query.from(instanceClass);
        LambdaWrapperContext<T> lambdaWrapperContext = LambdaWrapperContext.of(criteriaBuilder, query, root);
        where(lambdaWrapperContext);
        groupBy(lambdaWrapperContext);
        orderBy(lambdaWrapperContext);
        return entityManager.createQuery(query);
    }
}
